package connect_mgr

import (
	"common/p2p"
	"common/uuid"
	"errors"
	"fmt"
	cmap "github.com/orcaman/concurrent-map/v2"
	"github.com/zeebo/errs"
	"sync"
	"time"
)

var count uint32
var curPort uint16 = portStart
var portMtx sync.Mutex

const portStart = 10024

func getLocalTcpPort() uint16 {
	portMtx.Lock()
	defer portMtx.Unlock()

	curPort++
	if curPort > 0xffff {
		curPort = portStart
	}
	return curPort
}

type Connect struct {
	localPort  uint16
	clientaddr string
	remotePort uint16
	peerId     string
	//expired      time.Time
	portmapReady bool //事件10, 11
	channelReady bool //事件5,6
	p2pType      int
	NodeId       uuid.UUID
}
type ConnectInfo struct {
	Port     uint16
	Ip       string
	Ipv6     string
	DeviceId string
	P2pType  int
	NodeId   uuid.UUID
}

func init() {
	connects = cmap.New[*Connect]()
	//p2p.SetCallbackInterface(&eventCallbck)
}
func updatePortmapStatus(clientAddr string, s bool) {
	for item := range connects.IterBuffered() {
		if item.Val.clientaddr == clientAddr {
			item.Val.portmapReady = s
		}
	}
}

func updateChannelStatus(key string, s bool) {
	if v, ok := connects.Get(key); ok {
		v.channelReady = s
	}
}

func updateP2PType(key string, s int) {
	if v, ok := connects.Get(key); ok {
		//fmt.Println("udate p2p type", key, s)
		v.p2pType = s
	}
}

func checkP2PType(key string) (bool, bool) {
	if val, ok := connects.Get(key); ok {
		if p2pStatusAvailable(val.p2pType) {
			return val.channelReady, true
		}
	}
	return false, false
}

func delConnect(key string) {
	if v, ok := connects.Get(key); ok {
		p2p.RemoveConnect(v.peerId, v.localPort)
		connects.Remove(key)
	}
}
func GetP2PStatus(key string) string {
	if v, ok := connects.Get(key); ok {
		return channelString(v.p2pType)
	}
	return ""
}

// connects key deviceid, 每个device只连一次，只对应一个本地端口
var connects cmap.ConcurrentMap[string, *Connect]

var eventCallbck callback
var RelayStatus = errors.New("RelayStatus")

// Get 如果存在返回
func Get(peerId string, remotePort uint16) (uint16, error) {
	key := peerId
	if val, ok := connects.Get(key); ok {
		if val.remotePort != remotePort {
			return 0, errs.New("remote port not match")
		}
		if p2pStatusAvailable(val.p2pType) {
			return val.localPort, nil
		} else if val.p2pType == 131 {
			return val.localPort, RelayStatus
		}
		//if p2pStatusAvailable(val.p2pType) {
		//	return val.localPort, nil
		//} else if val.p2pType == 131 {
		//	return val.localPort, RelayStatus
		//}
	}
	return 0, errs.New("not found")
}
func p2pStatusAvailable(s int) bool {
	if s >= 128 && s <= 130 {
		return true
	}
	return false
}
func GetConnectedPort(count int) []uint16 {
	peer := make([]uint16, 0)
	for item := range connects.IterBuffered() {
		if p2pStatusAvailable(item.Val.p2pType) {
			peer = append(peer, item.Val.localPort)
			if len(peer) >= count {
				break
			} else {
				if connects.Count() < count {
					needNodesChan <- count - connects.Count()
				}
			}
		}
	}
	return peer
}
func GetConnectedConnectInfo(count int) []ConnectInfo {
	ouputs := make([]ConnectInfo, 0)

	for item := range connects.IterBuffered() {
		if p2pStatusAvailable(item.Val.p2pType) {
			ouputs = append(ouputs, ConnectInfo{
				DeviceId: item.Key,
				Port:     item.Val.localPort,
				P2pType:  item.Val.p2pType,
				NodeId:   item.Val.NodeId,
			})
			if len(DebugDeviceId) > 0 {
				output := ouputs[0]
				ouputs = ouputs[:0]
				for i := 0; i < count; i++ {
					ouputs = append(ouputs, output)
				}
			}
			if len(ouputs) >= count {
				break
			}
		}
	}

	return ouputs
}

const P2P_REJECT = -13

// addConnect, waitForP2P 是否等到p2p连接
func addConnect(key, peerId string, nodeid uuid.UUID, remotePort uint16, timeout int64, waitForP2P bool) (uint16, error) {
	var try = 3
	var result = 0
	var err error
	t1 := time.Now()
	for ; try > 0; try-- {
		tmpPort := getLocalTcpPort()
		result = p2p.AddConnect(peerId, remotePort, tmpPort, 0)
		err = errors.New(fmt.Sprintf("AddConnect result %d", result))
		if result == 0 {

			connects.Set(key, &Connect{
				localPort:  tmpPort,
				clientaddr: fmt.Sprintf("127.0.0.1:%d", tmpPort),
				remotePort: remotePort,
				peerId:     peerId,
				//expired:      time.Time{},
				channelReady: false,
				portmapReady: false,
				p2pType:      0,
				NodeId:       nodeid,
			})

			if !waitForP2P {
				return tmpPort, nil
			}

			//检查当前是否p2p或中转
			for {
				useTime := time.Now().Sub(t1).Milliseconds()

				channelReady, p2pread := checkP2PType(key)
				if channelReady && p2pread {
					fmt.Println(peerId, "addConnect ok")
					return tmpPort, nil
				}
				time.Sleep(time.Second / 2)
				if useTime > timeout {
					fmt.Println(peerId, "达到超时,useTime", useTime, "port", tmpPort)
					return tmpPort, errors.New("timeout")
				}
				//if !channelReady {
				//	fmt.Println(peerId, "!channelReady")
				//	return 0, errors.New("dev is offline")
				//}
			}

		} else {
			if result == P2P_REJECT { //可能是端口被占用
				// try next port
				continue
			} else {
				return 0, err
			}
		}
	}
	return 0, err
}
